package main

import (
	"fmt"
	"time"
)

/*
CSP  --> 不要通过共享内存来通信，而要通过通信来共享内存
channel 通道，本身就是一个队列，先进先出
channel 是引用类型  可以使用make初始化	make -- 可以对map、channel、slice
每个channel 只能放同一种类型的数据
使用channel 收发操作在不同的goroutine

写满的时候不能写，取空的时候不能取，很容易发生阻塞，很容易产生死锁

存放数据：ch <- 1
取数据：data := <- ch
 */

//func main(){
//	ch1 := make(chan int)
//	go senddata(ch1)
//	data := <- ch1
//	fmt.Println("get an number:",data)
//	data2 := <- ch1
//	fmt.Println("get an number:",data2)
//	time.Sleep(1 * time.Second)
//}
//
////chan int 是一个存放整数型的channel
//func senddata(ch chan int){
//	ch <- 1
//	fmt.Println("put an number:", 1)
//	ch <- 2
//	fmt.Println("put an number:", 2)
//}

//阻塞
//func main(){
//	fmt.Println("start main...")
//	ch1 := make(chan int)
//	go child(ch1)
//	fmt.Println("start child....")
//	<- ch1	//等子协程发送数据，主协程才能获取到数据，主协程会等子协程，起到阻塞当前主协程环境上下文作用
//	fmt.Println("get data finish!")
//}
//
//func child(ch chan int){
//	fmt.Println("this is child")
//	time.Sleep(5 * time.Second)
//	ch <- 1
//}

//判断通道是否关闭
//func main(){
//	ch1 := make(chan string)
//	go senddata(ch1)

	//方式一
	//for {
	//	data := <- ch1
	//	fmt.Println("从通道里读取到的数据:", data)
	//	//如果通道关闭，读取到的是默认值，string类型的默认值是空字符串，int类型的默认值是0
	//	if data == "" {
	//		break
	//	}
	//}

	//方式二
	//for {
	//	data, ok := <- ch1
	//	//ok 为 false 通道关闭
	//	if !ok {
	//		break
	//	}
	//	fmt.Println("获取到的数据:", data)
	//}

	//方式三	  range --> string,slice,map 会自动判断通道是否关闭，关闭了就break
//	for value := range ch1 {
//		fmt.Println("获取到的数据：", value)
//	}
//}
//
//func senddata(ch chan string){
//	for i:=0;i<3;i++{
//		ch <- fmt.Sprintf("send data: %d", i)
//	}
//	defer fmt.Println("数据发送完毕")
//	defer close(ch) //数据发送完毕，主动关闭通道，防止产生死锁引起程序退出
//}
//func main(){
//	fmt.Println("start main")
//	ch1 := make(chan int)
//}
//
//func child(){
//	i := 0
//	for {
//		fmt.Println("i is:", i)
//		time.Sleep(1 * time.Second)
//		i++
//
//	}
//}

//有缓冲的通道
//func main() {
//	ch1 := make(chan int, 6)
//	go senddata(ch1)
//	time.Sleep(5 * time.Second)
//	for data := range ch1 {
//		fmt.Println("读取数据", data)
//	}
//}
//
//
//func senddata(ch chan int) {
//	for i:=0; i<10; i++{
//		ch <- i
//		fmt.Println("往通道里面放数据:",i)
//	}
//}

//select 语句  -- 监听通道的数据
//挑选可通信的channel 执行代码
//同时可通信的channel

//没有可通信的channel就执行default
//类似于 switch case

//func main(){
//	ch1 := make(chan int)
//	ch2 := make(chan int)
//
//	go func(){
//		time.Sleep(2 * time.Second)
//		ch1 <- 100
//	} ()
//	go func(){
//		ch2 <- 200
//	}()
//	select {
//	case data := <- ch1:
//		fmt.Println("获取ch1的数据为:", data)
//	case data := <- ch2:
//		fmt.Println("获取ch2的数据为:", data)
//	default:
//		fmt.Println("default......")
//	}
//
//}


func main() {
	ch1 := make(chan int)
	ch2 := make(chan int)

	go senddata1(ch1)
	go senddata1(ch2)
	time.Sleep(1 * time.Second)
	flag := 0
	for {
		select {
		case data, ok:= <-ch1:
			if !ok {
				fmt.Println("数据读取完毕！")
				flag++
				break   //break 跳出select语句
			}
			fmt.Println("获取ch1的数据为:", data)
		case data,ok := <-ch2:
			if !ok {
				fmt.Println("数据读取完毕！")
				flag++
				break
			}
			fmt.Println("获取ch2的数据为:", data)
		//default:
		//	break
		}
		if flag == 2 {
			return
		}
	}
}

func senddata1(ch chan int) {
	for i:=0; i<10; i++ {
		ch <- i
	}
	defer close(ch)
}